{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "epochs = 50"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 7 - Aprendizaje Federado con Datos Federados\n",
    "\n",
    "Aquí introducimos una nueva herramienta para utilizar conjuntos de datos federados. Hemos creados una clase `FederatedDataset` para ser usada como cualquier clase PyTorch Dataset, y esta clase se manda a un *cargador de datos* (data loader) `FederatedDataLoader` que iterará sobre ella en una manera federada.\n",
    "\n",
    "Autores:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "- Théo Ryffel - GitHub: [@LaRiffle](https://github.com/LaRiffle)\n",
    "\n",
    "Traductores:\n",
    "- Arturo Márquez Flores - Twitter: [@arturomf94](https://twitter.com/arturomf94)\n",
    "- Ricardo Pretelt - Twitter: [@ricardopretelt](https://twitter.com/ricardopretelt)\n",
    "- Carlos Salgado - Github: [@socd06](https://github.com/socd06)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Utilizamos la *caja de arena* (sandbox) que descubrimos en una lección pasada."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch as th\n",
    "import syft as sy\n",
    "sy.create_sandbox(globals(), verbose=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Luego buscamos un conjunto de datos"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "boston_data = grid.search(\"#boston\", \"#data\")\n",
    "boston_target = grid.search(\"#boston\", \"#target\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cargamos un modelo y un optimizador"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_features = boston_data['alice'][0].shape[1]\n",
    "n_targets = 1\n",
    "\n",
    "model = th.nn.Linear(n_features, n_targets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí emitimos los datos extraidos en un `FederatedDataset`. Observa los trabajadores que tienen parte del conjunto de datos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Emite el resultado en BaseDatasets\n",
    "datasets = []\n",
    "for worker in boston_data.keys():\n",
    "    dataset = sy.BaseDataset(boston_data[worker][0], boston_target[worker][0])\n",
    "    datasets.append(dataset)\n",
    "\n",
    "# Construye el objeto FederatedDataset\n",
    "dataset = sy.FederatedDataset(datasets)\n",
    "print(dataset.workers)\n",
    "optimizers = {}\n",
    "for worker in dataset.workers:\n",
    "    optimizers[worker] = th.optim.Adam(params=model.parameters(),lr=1e-2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lo ponemos en un `FederatedDataLoader` y especificamos las opciones"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_loader = sy.FederatedDataLoader(dataset, batch_size=32, shuffle=False, drop_last=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y finalmente iteramos sobre las épocas. ¡Puedes ver lo similar que se esto a un entrenamiento de PyTorch puro y local!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for epoch in range(1, epochs + 1):\n",
    "    loss_accum = 0\n",
    "    for batch_idx, (data, target) in enumerate(train_loader):\n",
    "        model.send(data.location)\n",
    "        \n",
    "        optimizer = optimizers[data.location.id]\n",
    "        optimizer.zero_grad()\n",
    "        pred = model(data)\n",
    "        loss = ((pred.view(-1) - target)**2).mean()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        model.get()\n",
    "        loss = loss.get()\n",
    "        \n",
    "        loss_accum += float(loss)\n",
    "        \n",
    "        if batch_idx % 8 == 0:\n",
    "            print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tBatch loss: {:.6f}'.format(\n",
    "                epoch, batch_idx, len(train_loader),\n",
    "                       100. * batch_idx / len(train_loader), loss.item()))            \n",
    "            \n",
    "    print('Total loss', loss_accum)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# !Felicitaciones! - !Es hora de unirte a la comunidad!\n",
    "\n",
    "¡Felicitaciones por completar esta parte del tutorial! Si te gustó y quieres unirte al movimiento para preservar la privacidad, propiedad descentralizada de IA y la cadena de suministro de IA (datos), puedes hacerlo de las ¡siguientes formas!\n",
    "\n",
    "### Dale una estrella a PySyft en GitHub\n",
    "\n",
    "La forma más fácil de ayudar a nuestra comunidad es por darle estrellas a ¡los repositorios de Github! Esto ayuda a crear consciencia de las interesantes herramientas que estamos construyendo.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### ¡Únete a nuestro Slack!\n",
    "\n",
    "La mejor manera de mantenerte actualizado con los últimos avances es ¡unirte a la comunidad! Tú lo puedes hacer llenando el formulario en [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### ¡Únete a un proyecto de código!\n",
    "\n",
    "La mejor manera de contribuir a nuestra comunidad es convertirte en un ¡contribuidor de código! En cualquier momento puedes ir al _Github Issues_ de PySyft y filtrar por \"Proyectos\". Esto mostrará todos los tiquetes de nivel superior dando un resumen de los proyectos a los que ¡te puedes unir! Si no te quieres unir a un proyecto, pero quieres hacer un poco de código, también puedes mirar más mini-proyectos \"de una persona\" buscando por Github Issues con la etiqueta \"good first issue\".\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donar\n",
    "\n",
    "Si no tienes tiempo para contribuir a nuestra base de código, pero quieres ofrecer tu ayuda, también puedes aportar a nuestro *Open Collective\"*. Todas las donaciones van a nuestro *web hosting* y otros gastos de nuestra comunidad como ¡hackathons y meetups!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
